"use strict";

//**********************************************
// engine class (all engine variables go here)
//**********************************************
function Engine() { }
Engine.prototype.canvas = null;
Engine.prototype.buffer = document.createElement("canvas");
Engine.prototype.sample = new Sample();
Engine.prototype.text = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''];
Engine.prototype.mousedown = false;
Engine.prototype.doublebuffer = false;
Engine.prototype.maxFPS = 0;
Engine.prototype.fpsCtr = 0;
Engine.prototype.fpsLastTime = new Date().getTime();
Engine.prototype.camera = { x: 0, y: 0 };
Engine.prototype.nexttime = 0;
Engine.prototype.sounds = {};
Engine.prototype.currenttile = 0;
Engine.prototype.draggingtiles = 0;
Engine.prototype.tilesize = 32;
Engine.prototype.player = null;
Engine.prototype.initialized = true;
Engine.prototype.dumpsamples = false;
Engine.prototype.tilesonscreen = { x: 1, y: 1 };
Engine.prototype.mousepos = { x: -100, y: -100 };
Engine.prototype.now = new Date().getTime();
Engine.prototype.fps = 0;
Engine.prototype.speed = 400;
Engine.prototype.mode = null;
Engine.prototype.backcolor = "#565656";
Engine.prototype.dirtyrects = [];
Engine.prototype.sounds = {};
Engine.prototype.sprites = [];
Engine.prototype.startupsound = null; // 'data/Sounds/fluteloop.wav';

Engine.prototype.Initialize = function (canvas)
{
    this.canvas = canvas;
    canvas.addEventListener('mousemove', this.On_MouseMove.bind(this), false);
    canvas.addEventListener('mouseenter', this.On_MouseEnter.bind(this), false);
    canvas.addEventListener('mouseleave', this.On_MouseLeave.bind(this), false);
    canvas.addEventListener('mousedown', this.On_MouseDown.bind(this), false);
    canvas.addEventListener('mouseup', this.On_MouseUp.bind(this), false);
    canvas.addEventListener('keydown', this.On_KeyDown.bind(this), true);
    canvas.focus();

    // Make canvas fill screen
    this.On_WindowResize();

    // Create some sprites
    this.sprites = [
            new ImageSprite({
                name: 'Bexhill', moverandom: false, x: 64, y: 64, z: 1, src: 'bextiles', animation: 'standdown',
                pathcolor: "rgba(255, 255, 0, .4)",
                animations: {
                    walkdown: [0, 1, 0, 2], walkright: [3, 4, 3, 5], walkleft: [6, 7, 6, 8], walkup: [9, 10, 9, 11],
                    standdown: [0], standright: [3], standleft: [6], standup: [9]
                }
            }),
            new ImageSprite({
                name: 'Hoot', moverandom: false, pathcolor: "rgba(0, 255, 255, .4)", isplayer: true, x: 64, y: 64, z: 1,
                src: 'hoot', animation: 'standdown',
                animations: {
                    walkdown: [1, 0, 1, 2], walkright: [3, 4, 3, 5], walkleft: [6, 7, 6, 8], walkup: [9, 10, 9, 11],
                    standdown: [0], standright: [3], standleft: [6], standup: [9]
                }
            }),
            new ImageSprite({
                name: 'lady', moverandom: false, x: 384, y: 416, src: 'lady', animation: 'standdown',
                animations: {
                    walkdown: [1, 0, 1, 2], walkright: [7, 6, 7, 8], walkleft: [4, 3, 4, 5], walkup: [10, 9, 10, 11],
                    standdown: [1], standright: [7], standleft: [4], standup: [10]
                },
                pathcolor: "rgba(255, 0, 255, .4)"
            }),
            new Label({ x: 10, y: 30, text: 'camera', forecolor: "#FFFF00", events: { camera: 0 } }),
            new Label({ x: 288, y: 10, text: 'Decorations', forecolor: "#FFFF00", events: { decorations: 0 } }),
            new Label({ x: 10, y: 10, text: 'fps', events: { fps: 0 } }),
            //new Label({ x: 10, y: 30, text: 'speed', events: { speed: 0 } }),
            new Label({ x: 704, y: 0, textalign: 'right', text: 'mouseloc', forecolor: "rgba(255, 255, 0, .9)", events: { xy: 0 } }),
            //new Label({ x: 20, y: 100, text: 'jj', forecolor: "#FFFFFF", multiline: true, events: { keypress: 0 } }),
    ];

}
Engine.prototype.DetectGlobals = function ()
{
    var ignoreList = ['console', 'log', 'useanimframe', 'Background', 'BallSprite', 'Button', 'Combat', 'Copy', 'Cryo', 'CursorSprite', 'Effect', 'Ego', 'Engine', 'GameObject', 'ImageSprite', 'Label', 'Library', 'Load', 'Monster', 'On_Tick', 'On_WindowLoad', 'Pathfinder', 'Player', 'Revive', 'Sample', 'Save', 'Sprite', 'TextSprite', 'TileMap', 'Tileset', 'UI',
    '$', 'DP_jQuery_1364662086599', 'jQuery', 'opener',
    'CreateCanvas', 'SetUpLogging',
    'combat', 'engine', 'font', 'library', 'requestAnimFrame', 'tilemap', 'txtOutput'
    ];
    var differences = {},
        globals = {},
        i = ignoreList.length,
        iframe = document.createElement('iframe');
    while (i--)
    {
        globals[ignoreList[i]] = 1
    }
    for (i in window)
    {
        differences[i] = {
            'type': typeof window[i],
            'val': window[i]
        }
    }
    iframe.style.display = 'none';
    document.body.appendChild(iframe);
    iframe.src = 'about:blank';
    iframe = iframe.contentWindow || iframe.contentDocument;
    for (i in differences)
    {
        if (typeof iframe[i] != 'undefined') delete differences[i];
        else if (globals[differences[i].type]) delete differences[i]
    }
    ignoreList.concat('addEventListener,document,location,navigator,window'.split(','));
    i = ignoreList.length;
    while (--i >= 0)
        delete differences[ignoreList[i]]

    var all = [];
    for (var key in differences)
    {
        if (!(key.startsWith('DP_jQuery')))
            all.push(key);
    }
    all.sort();
    var s = '';
    if (console.dir)
        console.dir(all);
    for (var i = 0; i < all.length; i++)
        s += ' ' + all[i];
    log('Globals detected: ' + s);

}
Engine.prototype.Test = function ()
{
    var obj = new ImageSprite();
    var s = '\nImageSprite:\n';
    for (var key in obj)
    {
        if (typeof obj[key] != 'function')
            s += key + ': ' + obj[key] + ', '
    }
    log(s + '\n\n');
}
Engine.prototype.MoveEgo = function (direction)
{
    var adj = { right: [1, 0], left: [-1, 0], up: [0, -1], down: [0, 1] };
    this.MoveTo(this.player, this.player.x - this.camera.x + adj[direction][0] * 32, this.player.y - this.camera.y + adj[direction][1] * 32);
}
Engine.prototype.MoveToTile = function (x, y)
{
    x = Math.floor(x);
    y = Math.floor(y);
    var tilesw = tilemap.Width();
    var tilesh = tilemap.Height();
    x = x.clamp(0, tilesw - 1);
    y = y.clamp(0, tilesh - 1);
    var px = Math.floor(this.player.x / this.tilesize);
    var py = Math.floor(this.player.y / this.tilesize);
    var newpx = Math.floor(x).clamp(0, tilesw - 1);
    var newpy = Math.floor(y).clamp(0, tilesh - 1);
    this.player.SetPath(px, py, newpx, newpy);
}
Engine.prototype.MoveTo = function (ego, x, y)
{
    var px = Math.floor(ego.x / this.tilesize);
    var py = Math.floor(ego.y / this.tilesize);
    var origpos = ego.x + ',' + ego.y + ' (' + px + ',' + py + ')';
    var tilesw = tilemap.Width();
    var tilesh = tilemap.Height();
    var newpx = Math.floor(x / this.tilesize).clamp(0, tilesw - 1);
    var newpy = Math.floor(y / this.tilesize).clamp(0, tilesh - 1);
    var newx = Math.floor(newpx * 32);
    var newy = Math.floor(newpy * 32);
    ego.SetPath(px, py, newpx, newpy);
    var newpos = newx + ',' + newy + ' (' + newpx + ',' + newpy + ')';
    this.SetCamera();
    //log('Moving ' + ego.name + ' from ' + origpos + ' to ' + newpos + '(camera is ' + this.camera.x + ',' + this.camera.y + ')');
}
Engine.prototype.Set = function (property, value)
{
    this[property] = value;
    log('Setting engine.' + property + ' to ' + value);
}
Engine.prototype.ClearDirtyRects = function ()
{
    this.dirtyrects = [];
}
Engine.prototype.PlaySound = function (soundfilename, loop)
{
    try
    {
        if (!soundfilename) return;
        var tag = '';
        var formats = ['mp3', 'wav', 'ogg'];
        var pos = soundfilename.lastIndexOf(".");
        var docsounds = document.getElementById('sounds');
        if (!docsounds)
        {
            docsounds = document.createElement('div');
            docsounds.id = 'sounds';
            document.body.appendChild(docsounds);
        }
        if (pos > -1)
            soundfilename = soundfilename.substr(0, pos);
        var channels = this.sounds[soundfilename];
        if (!channels)
        {
            var snd = document.createElement('audio');
            for (var i = 0; i < formats.length; i++)
            {
                var source = document.createElement('source');
                source.type = 'audio/' + formats[i];
                source.src = soundfilename + '.' + formats[i];
                snd.appendChild(source);
            }
            var channels = [];
            for (var k = 0; k < 10; k++)
            {
                var snd2 = snd.cloneNode(true);
                snd2.id = soundfilename + k;
                channels.push(snd2);
            }
            this.sounds[soundfilename] = channels;
            docsounds.appendChild(snd);
            tag = 'new';
        }
        else
            tag = 'cached';
        for (var i = 0; i < 10; i++)
        {
            if (channels[i].ended == true || channels[i].currentTime == 0)
            {
                if (loop)
                    channels[i].loop = true;
                else
                    channels[i].loop = false;
                if (channels[i].currentTime > 0)
                    channels[i].currentTime = 0;
                channels[i].play();
                log('Playing ' + tag + ' sound ' + soundfilename + ' on channel ' + i);
                return;
            }
        }
    } catch (ex)
    {
        log('Error when playing ' + soundfilename + ' - ' + ex.message + '\n' + ex.toString());
    }
}

Engine.prototype.Render = function ()
{
    if (this.dirtyrects.length == 0)
    {
        //log('Setting dirtyrects to entire screen size');
        this.dirtyrects[[0, 0, this.canvas.width, this.canvas.height]];
    }
    this.now = new Date().getTime();
    if (this.now < this.nexttime)
        return;
    this.nexttime = this.now + Math.round(1000 / this.speed);
    this.sample.Label("engine.Render", "start Render");
    this.fpsCtr += 1;
    if ((this.now - this.fpsLastTime) > 1000)
    {
        // Calculate the FPS	
        this.fps = this.fpsCtr;
        this.fpsCtr = 0;
        this.fpsLastTime = this.now;
        if (this.maxFPS < this.fps)
            this.maxFPS = this.fps;
    }

    this.sample.Label("engine.Render", "calculate fps");

    // Tick the library
    library.Tick(this.now);
    this.sample.Label("engine.Render", "tick the library");

    // Tick the sprites
    for (var i = 0; i < this.sprites.length; i++)
        this.sprites[i].Tick();
    this.sample.Label("engine.Render", "tick the sprites");
    this.SetCamera();

    // Get the graphic context (canvas or offscreen buffer)	
    var ctx = null;
    if (this.doublebuffer)
        ctx = this.buffer.getContext('2d');
    else
        ctx = this.canvas.getContext('2d');
    ctx.beginPath();
    this.sample.Label("engine.Render", "get context");
    ctx.fillStyle = this.backcolor;
    //ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
    this.sample.Label("engine.Render", "fill with black");

    // Draw the tilemap
    tilemap.Draw(ctx, this.camera);
    this.sample.Label("engine.Render", "draw the tiles");

    // Draw the paths
    pathing.DrawPaths(ctx);

    // Sort the sprites
    this.sprites.sort(function (a, b)
    {
        if (a.z > b.z) return 1;
        else if (a.z < b.z) return -1;
        else
        {
            if (a.y > b.y) return 1;
            else if (a.y < b.y) return -1;
            else
            {
                if (a.x > b.x) return 1;
                else if (a.x < b.x) return -1;
                else return 0;
            }
        }
    });
    this.sample.Label("engine.Render", "sort the sprites");

    // Draw the sprites
    for (var i = 0; i < this.sprites.length; i++)
        this.sprites[i].DrawMe(ctx);
    this.sample.Label("engine.Render", "draw the sprites");

    // Copy the backbuffer to the screen
    if (this.doublebuffer)
    {
        ctx = this.canvas.getContext('2d');
        ctx.drawImage(this.buffer, 0, 0);
        this.sample.Label('engine.Render', 'copy backbuffer to canvas');
    }

    this.sample.Label("engine.Render", "end Render");

}

Engine.prototype.GetLastLine = function ()
{
    return this.text[this.text.length - 1];
}
Engine.prototype.GetAllLines = function ()
{
    return this.text.slice(0);
}

Engine.prototype.SetSpeed = function (speedAsPercent)
{
    engine.speed = Math.round(speedAsPercent * 1000 / 100);
    log('Setting engine speed to ' + engine.speed + ', this=' + typeof this);
}
Engine.prototype.SetCamera = function ()
{
    var mapwidth = tilemap.Width() * this.tilesize;
    var mapheight = tilemap.Height() * this.tilesize;
    var maxMapX = Math.max(0, mapwidth - this.canvas.width);
    var maxMapY = Math.max(0, mapheight - this.canvas.height);
    var cx = 0;
    var cy = 0;
    if (this.player)
    {
        cx = Math.floor(this.player.x - this.canvas.width / 2);
        cy = Math.floor(this.player.y - this.canvas.height / 2);
    }
    var newcamera = { x: cx.clamp(0, maxMapX), y: cy.clamp(0, maxMapY) };
    if ((this.camera.x != newcamera.x) || (this.camera.y != newcamera.y))
    {
        this.camera.x = newcamera.x;
        this.camera.y = newcamera.y;
        //log("Setting camera to " + this.camera.x + ", " + this.camera.y +
        //    ', map = ' + mapwidth + ',' + mapheight +
        //    ', canvas = ' + this.canvas.width + ',' + this.canvas.height +
        //    ', max = ' + maxMapX + ',' + maxMapY);
        this.ClearDirtyRects();
    }
}

Engine.prototype.On_ClickTile = function (e, obj, pathname)
{
    var rect = obj.getBoundingClientRect();
    var pos = { x: e.clientX - rect.left + obj.scrollLeft, y: e.clientY - rect.top + obj.scrollTop };
    var x = Math.floor(pos.x / this.tilesize);
    var y = Math.floor(pos.y / this.tilesize);
    var numtilesx = Math.floor(obj.width / this.tilesize);
    this.currenttile = [pathname, (y * numtilesx) + x];
    log('Setting current tile to ' + this.currenttile[0] + ' ' + this.currenttile[1]);
}
Engine.prototype.On_WindowResize = function ()
{
    var w = window.innerWidth;
    var h = window.innerHeight;
    var parent = this.canvas.parentElement;
    if (parent == null)
        return;
    var r = parent.getBoundingClientRect();
    var cw = r.right - r.left;
    var ch = r.bottom - r.top;

    if (cw > 0 && ch > 0)
    {
        log('Resizing window to ' + w + ',' + h + ' and canvas to ' + cw + ', ' + ch + ' (was ' + canvas.width + ', ' + canvas.height + ')');
        this.canvas.width = cw;
        this.canvas.height = ch;
        this.buffer.width = cw;
        this.buffer.height = ch;
        this.screenwidth = cw;
        this.screenheight = ch;
        this.tilesonscreen = { x: Math.floor(this.canvas.width / this.tilesize), y: Math.floor(this.canvas.height / this.tilesize) };
        this.ClearDirtyRects();
        tilemap.Refresh();
    } else
    {
        log('parentElement = ' + parent.toString());
        log('Skipping resize because parentElement of canvas width is ' + cw + ' and height is ' + ch);
    }

}
Engine.prototype.On_MouseEnter = function (evt)
{
    this.mousedown = false;
    this.On_MouseMove(evt);
}
Engine.prototype.On_MouseLeave = function (evt)
{
    this.mousedown = false;
    this.On_MouseMove(evt);
}
Engine.prototype.On_MouseMove = function (evt)
{
    var rect = this.canvas.getBoundingClientRect();
    var pos = {
        x: evt.clientX - rect.left + this.canvas.scrollLeft,
        y: evt.clientY - rect.top + this.canvas.scrollTop
    };
    this.mousepos = pos;
    if (this.mousedown)
    {
        if (this.mode == 'design')
        {
            var cx = Math.round(this.camera.x / this.tilesize);
            var cy = Math.round(this.camera.y / this.tilesize);
            var x = Math.floor(pos.x / this.tilesize) + cx;
            var y = Math.floor(pos.y / this.tilesize) + cy;
            log('tilemap.PlaceTile(' + x + ', ' + y + ', ' + this.currenttile + ')');
            tilemap.PlaceTile(x, y, this.currenttile);
        }

    }
}
Engine.prototype.On_MouseDown = function (evt)
{
    this.mousedown = true;
    var rect = this.canvas.getBoundingClientRect();
    var pos = {
        x: evt.clientX - rect.left + this.canvas.scrollLeft,
        y: evt.clientY - rect.top + this.canvas.scrollTop
    };
    if (this.mode != 'design')
    {
        this.MoveTo(this.player, engine.camera.x + pos.x, engine.camera.y + pos.y);
    }
    else
        this.On_MouseMove(evt);
}
Engine.prototype.On_MouseUp = function (evt)
{
    this.mousedown = false;
}
Engine.prototype.On_KeyDown = function (evt)
{
    evt = evt || window.event;
    var direction = '';
    switch (evt.keyCode)
    {
        case 39:
            direction = 'right';
            break;
        case 37:
            direction = 'left';
            break;
        case 40:
            direction = 'down';
            break;
        case 38:
            direction = 'up';
            break;
    }
    if (direction.length > 0)
    {
        this.MoveEgo(direction);
        evt.preventDefault();
        return false;
    }
    return true;
}

